﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.IO;
using System.Diagnostics;

namespace VSTSUnitTestReport
{
    public class GenerateReport
    {
        System.Diagnostics.EventLog eventLog;

        public GenerateReport(System.Diagnostics.EventLog eventLog)
        {
            this.eventLog = eventLog;
        }

        StreamWriter sw = null;
        int _quiet = 0;

        public bool VerifyInputs(string[] argv, string message, ref string input, ref string output, ref string codecoverage, ref int quiet)
        {
            try
            {
                int index = 0;
                if (argv.Length == 4)
                {
                    if (argv[0] != "/q")
                    {
                        message += "\nUnexpected argument (expecting /q): " + argv[0];
                        Console.WriteLine(message);
                        eventLog.WriteEntry(message, EventLogEntryType.Error);
                        return false;
                    }
                    else
                    {
                        _quiet = 1;
                        quiet = 1;
                        index = 1;
                    }

                }
                input = argv[index++];
                output = argv[index++];

                if (File.Exists(input) == false)
                {
                    string message1 = "Could not find input file: " + input;
                    if (_quiet == 0)
                    {
                        Console.WriteLine(message1);
                    }
                    eventLog.WriteEntry(message1, EventLogEntryType.Error);
                    return false;
                }
                if (File.Exists(output))
                {
                    File.Delete(output);
                }

                try
                {
                    codecoverage = argv[index];
                    if (!File.Exists(codecoverage)) { codecoverage = String.Empty; }
                }
                catch
                {
                    codecoverage = String.Empty;
                }

                XmlDocument doc = new XmlDocument();
                doc.Load(input);
            }
            catch (Exception e)
            {
                if (_quiet == 0)
                {
                    Console.WriteLine(e.Message + "\n" + e.StackTrace);
                }
                eventLog.WriteEntry(e.Message + "\n" + e.StackTrace, EventLogEntryType.Error);
                return false;
            }
            return true;
        }

        public int MyMain(string input, string output, string codecoverageXML)
        {
            try
            {
                InitOutput(output);
                if (File.Exists(input) != true)
                {
                    return 1;
                }

                //change the first tag to have no attributes - VSTS2008

                string tempXml = Path.GetTempPath() + "\\temp.xml";
                File.Copy(input, tempXml, true);

                XmlDocument doc = new XmlDocument();
                doc.Load(tempXml);
                XmlElement root = doc.DocumentElement;
                root.Attributes.RemoveAll();
                doc.Save(tempXml);

                string file = File.ReadAllText(tempXml);
                if (file.Contains("xmlns=\"http://microsoft.com/schemas/VisualStudio/TeamTest/2006\""))
                {
                    file = file.Replace("xmlns=\"http://microsoft.com/schemas/VisualStudio/TeamTest/2006\"", "");
                }
                else if (file.Contains("xmlns=\"http://microsoft.com/schemas/VisualStudio/TeamTest/2010\""))
                {
                    file = file.Replace("xmlns=\"http://microsoft.com/schemas/VisualStudio/TeamTest/2010\"", "");
                }

                File.WriteAllText(tempXml, file);

                doc = new XmlDocument();
                doc.Load(tempXml);

                // Summary Table
                summary s = XmlManipulations.GetSummary(doc);
                SummaryTable(s);

                // Code coverage table
                if (codecoverageXML != String.Empty)
                {
                    Module[] m = XmlManipulations.GetCCData(codecoverageXML);
                    if (m != null)
                    {
                        CCTable(m);
                    }
                }

                // Unit test reports:
                // Some unit tests may be run as part of an Ordered test
                if (XmlManipulations.GetResultCount(doc, ResultType.UnitTestResult) > 0)
                {
                    // Get all test dlls
                    string[] testDlls = XmlManipulations.GetTestDlls(doc);
                    if (testDlls.Length > 0)
                    {
                        // Get details for each test dlls
                        testProjectDetails[] allDetails = new testProjectDetails[testDlls.Length];
                        for (int i = 0; i < allDetails.Length; i++)
                        {
                            allDetails[i].testDll = testDlls[i];
                            string[] testNames = XmlManipulations.GetTestNames(doc, testDlls[i]);
                            ////add description call
                            string[] testDescriptions = XmlManipulations.GetTestDescription(doc, testDlls[i]);
                            allDetails[i].tests = new Test[testNames.Length];
                            allDetails[i].count = testNames.Length;
                            for (int j = 0; j < testNames.Length; j++)
                            {
                                allDetails[i].tests[j].Name = testNames[j];
                                allDetails[i].tests[j].Description = testDescriptions[j];
                            }
                            XmlManipulations.GetTestPassFails(doc, ref allDetails[i]);
                        }
                        // Summary table for test dlls
                        TestProjectSummaryTable(allDetails);
                        // Details for individual unit tests in each test dll
                        TestDetailsByDllTable(allDetails);
                    }
                }

                // WebTest reports:
                Test[] webTests = XmlManipulations.GetTestDetails(doc.DocumentElement, ResultType.WebTestResult); // GetWebTestDetails(doc);
                if (webTests.Length > 0)
                {
                    // Add Table for web tests:
                    TestDetailsTable(webTests, "Web Tests:");
                    //TestDetailsWebTests(webTests);
                }

                // Manual test reports:
                Test[] manualTests = XmlManipulations.GetTestDetails(doc.DocumentElement, ResultType.ManualTestResult); //.GetManualTestDetails(doc);
                if (manualTests.Length > 0)
                {
                    // Add Table for manual tests:
                    //TestDetailsManualTests(manualTests);
                    TestDetailsTable(manualTests, "Manual Tests:");
                }

                // Generic test reports:
                Test[] genericTests = XmlManipulations.GetTestDetails(doc.DocumentElement, ResultType.GenericTestResult);
                if (genericTests.Length > 0)
                {
                    TestDetailsTable(genericTests, "Generic tests:");
                }

                // Load test reports:
                Test[] loadTests = XmlManipulations.GetTestDetails(doc.DocumentElement, ResultType.LoadTestResult);
                if (loadTests.Length > 0)
                {
                    TestDetailsTable(loadTests, "Load tests:");
                }

                // Ordered test reports:
                Test[] orderedTests = XmlManipulations.GetTestDetails(doc.DocumentElement, ResultType.TestResultAggregation);
                if (orderedTests.Length > 0)
                {
                    TestDetailsTable(orderedTests, "Ordered tests:");
                    // List results of sub-tests for each ordered test:
                    foreach (Test orderedTest in orderedTests)
                    {
                        // Get the parent orderedTest node:
                        XmlNode orderedParent = XmlManipulations.GetOrderedTestsRoot(doc, orderedTest.Name);
                        if (null != orderedParent)
                        {
                            Test[] orderedSubTests = XmlManipulations.GetTestDetails(orderedParent, ResultType.TestResultAggregationSubTests);
                            if (orderedSubTests.Length > 0)
                            {
                                TestDetailsTable(orderedSubTests, "Individual tests in " + orderedTest.Name + " (ordered test):");
                            }

                        }
                    }
                }
            }
            catch (Exception e)
            {
                if (_quiet == 0)
                {
                    Console.WriteLine(e.Message + "\n" + e.StackTrace);
                }
                //eventLog.WriteEntry(e.Message + "\n" + e.StackTrace, EventLogEntryType.Error);
                return 1;
            }
            finally
            {
                if (sw != null)
                {
                    sw.WriteLine("</body>");
                    sw.WriteLine("</html>");
                    sw.Close();
                }

            }
            // delete temp file


            return 0;

        }

        private void InitOutput(string output)
        {
            if (File.Exists(output))
            {
                File.Delete(output);
            }
            sw = new StreamWriter(output);
            sw.WriteLine("<html>");
            sw.WriteLine("<head>");
            sw.WriteLine("<style type=\"text/css\">");
            sw.WriteLine("body { font:normal 68% verdana,arial,helvetica; color:#000000; }");
            sw.WriteLine("span.info { font-size:smaller; font-weight:normal; color:blue; font-style:italic; font-family:Arial;}");
            sw.WriteLine("span.covered { background: #00df00; display: inline-block;height: 9px}");
            sw.WriteLine("span.uncovered { background: #df0000; display: inline-block; height: 9px}");
            sw.WriteLine("span.ignored { background: #CCCCCC; display: inline-block; height: 9px}");
            sw.WriteLine("td { FONT-SIZE: 68%; BORDER-BOTTOM: #dcdcdc 1px solid; BORDER-RIGHT: #dcdcdc 1px solid;}");
            sw.WriteLine("p {	line-height:1.5em; margin-top:0.5em; margin-bottom:1.0em;}");
            sw.WriteLine("h1 { MARGIN: 0px 0px 5px; FONT: 165% verdana,arial,helvetica;}");
            sw.WriteLine("h2 { MARGIN-TOP: 1em; MARGIN-BOTTOM: 0.5em; FONT: bold 125% verdana,arial,helvetica;}");
            sw.WriteLine("h3 { MARGIN-BOTTOM: 0.5em; FONT: bold 115% verdana,arial,helvetica; }");
            sw.WriteLine("h4 { MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica;}");
            sw.WriteLine("h5 { MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica;}");
            sw.WriteLine("h6 { MARGIN-BOTTOM: 0.5em; FONT: bold 100% verdana,arial,helvetica;}");
            sw.WriteLine(".Error { font-weight:bold;}");
            sw.WriteLine(".Failure { font-weight:bold; color:red;}");
            sw.WriteLine(".Success { font-weight:bold; color:blue;}");
            sw.WriteLine(".Ignored { font-weight:bold;}");
            sw.WriteLine(".FailureDetail { font-size: -1; padding-left: 2.0em; background:#cdcdcd;}");
            sw.WriteLine(".Pass { padding-left:2px;}");
            sw.WriteLine(".TableHeader { background: #efefef; color: #000; font-weight: bold; horizontal-align: center; }");
            sw.WriteLine("a:visited { color: #0000ff;} a { color: #0000ff;}");
            sw.WriteLine("a:active { color: #800000;} a.summarie { color:#000; text-decoration: none;}");
            sw.WriteLine("a.summarie:active { color:#000; text-decoration: none;}");
            sw.WriteLine("a.summarie:visited { color:#000; text-decoration: none;}");
            sw.WriteLine(".description { margin-top:1px; padding:3px; background-color:#dcdcdc; color:#000; font-weight:normal;}");
            sw.WriteLine(".method{ color:#000; font-weight:normal; padding-left:5px;}");
            sw.WriteLine("a.method{ text-decoration: none; color:#000; font-weight:normal; padding-left:5px;}");
            sw.WriteLine("a.Failure { font-weight:bold; color:red; text-decoration: none;}");
            sw.WriteLine("a.Failure:visited { font-weight:bold; color:red; text-decoration: none;}");
            sw.WriteLine("a.Failure:active { font-weight:bold; color:red; text-decoration: none;}");
            sw.WriteLine("a.error { font-weight:bold; color:red;}");
            sw.WriteLine("a.error:visited { font-weight:bold; color:red;}");
            sw.WriteLine("a.error:active { font-weight:bold; color:red;}");
            sw.WriteLine("a.ignored { font-weight:bold; text-decoration: none; padding-left:5px;}");
            sw.WriteLine("a.ignored:visited { font-weight:bold; text-decoration: none; padding-left:5px;}");
            sw.WriteLine("a.ignored:active { font-weight:bold; text-decoration: none; padding-left:5px;}");
            sw.WriteLine("</style>");
            AddToggleScript();
            sw.WriteLine("</head>");
            sw.WriteLine("<body>");
            sw.WriteLine("<h2 id=\"top\" class=\"title\">VSTS Test results <span class=\"info\"></span></h2>");
            sw.WriteLine("<hr />");
        }

        private void AddToggleScript()
        {
            sw.WriteLine("<script language=\"JavaScript\">");
            sw.WriteLine("function Toggle(id) {");
            sw.WriteLine("var element = document.getElementById(id);");
            sw.WriteLine("if ( element.style.display == \"none\" )");
            sw.WriteLine("element.style.display = \"block\";");
            sw.WriteLine("else ");
            sw.WriteLine("element.style.display = \"none\";");
            sw.WriteLine("}");
            sw.WriteLine("</script>");
        }

        private void SummaryTable(summary s)
        {
            int failed = s.executed - s.passed;
            double successRate = Math.Round(s.passed * 100.0 / s.total, 2);
            // Total width of bar: 200px
            int covered = (int)Math.Round(successRate * 2);
            int uncovered = (int)(Math.Round((s.executed - s.passed) * 100.0 / s.total) * 2);
            int ignored = 200 - covered - uncovered;

            sw.WriteLine("<h3>Summary: " + s.execDate + "</h3>");
            sw.WriteLine("<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\" width=\"95%\" style=\"border: #dcdcdc 1px solid;\">");
            sw.WriteLine("<tr valign=\"top\" class=\"TableHeader\">");
            sw.WriteLine("<td width=\"50px\"><b>Tests</b></td>");
            sw.WriteLine("<td width=\"70px\"><b>Executed</b></td>");
            sw.WriteLine("<td width=\"70px\"><b>Failed</b></td>");
            sw.WriteLine("<td colspan=\"2\"><b>Success Rate</b></td>");
            sw.WriteLine("<td width=\"70px\" nowrap=\"nowrap\"><b>Time(s)</b></td></tr>");
            sw.WriteLine("<tr valign=\"top\" class=\"Success\">");
            sw.WriteLine("<td>" + s.total.ToString() + " </td>");
            sw.WriteLine("<td>" + s.executed.ToString() + "</td>");
            sw.WriteLine("<td>" + failed.ToString() + "</td>");
            sw.WriteLine("<td nowrap=\"nowrap\" width=\"70px\">" + successRate + "%</td>");
            sw.WriteLine("<td><span class=\"covered\" style=\"width:" + covered + "px\"/><span class=\"uncovered\" style=\"width:" + uncovered + "px\"/>");
            sw.WriteLine("<span class=\"ignored\" style=\"width:" + ignored + "px\"></span></td>");
            sw.WriteLine("<td>" + s.time + "</td>"); // time
            sw.WriteLine("</tr></table>");
        }

        private void TestProjectSummaryTable(testProjectDetails[] allDetails)
        {
            sw.WriteLine("<h2>Test Container Summary</h2>");
            sw.WriteLine("<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\" width=\"95%\">");
            sw.WriteLine("<tr class=\"TableHeader\" valign=\"top\">");
            sw.WriteLine("<td width=\"70%\" colspan=\"3\"><b>Name</b></td>");
            sw.WriteLine("<td width=\"10%\"><b>Tests</b></td>");
            sw.WriteLine("<td width=\"10%\"><b>Failures</b></td>");
            sw.WriteLine("<td width=\"10%\" nowrap=\"nowrap\"><b>Time(s)</b></td></tr>");
            foreach (testProjectDetails t in allDetails)
            {
                double passRate = Math.Round((t.count - t.fail) * 100.0 / t.count, 2);
                int covered = (int)passRate * 2;
                int uncovered = 200 - covered;
                sw.WriteLine("<tr valign=\"top\" class=\"Failure\"><td width=\"25%\">");
                // CSS for test dlls w/ errors
                if (t.fail == 0)
                {
                    sw.WriteLine("<a href=\"#" + t.testDll + "\">" + t.testDll + "</a></td>");
                }
                else
                {
                    sw.WriteLine("<a href=\"#" + t.testDll + "\" class=\"Failure\">" + t.testDll + "</a></td>");
                }
                sw.WriteLine("<td nowrap=\"nowrap\" width=\"6%\" align=\"right\"><b>" + passRate + " %</b></td>");
                sw.WriteLine("<td width=\"20%\" height=\"9px\"><span class=\"covered\" style=\"width:" + covered + "px\"/><span class=\"uncovered\" style=\"width:" + uncovered + "px\"/></td>");
                sw.WriteLine("<td>" + t.count + "</td><td>" + t.fail + "</td>");
                // Calculate total time:
                double time = 0;
                for (int i = 0; i < t.tests.Length; i++)
                {
                    time = time + t.tests[i].Time;
                }
                time = Math.Round(time, 3);
                sw.WriteLine("<td>" + time.ToString() + "</td></tr>");
            }
            sw.WriteLine("</table>");
            sw.WriteLine("<hr size=\"1\" width=\"95%\" align=\"left\"></hr>");

        }

        private void TestDetailsByDllTable(testProjectDetails[] allDetails)
        {
            foreach (testProjectDetails t in allDetails)
            {
                TestDetailsTable(t.tests, t.testDll);
            }
        }

        private void TestDetailsTable(Test[] tests, string tableName)
        {
            sw.WriteLine("<h3 id=" + tableName + ">" + tableName + "</h3>");
            sw.WriteLine("<table border=\"0\" cellpadding=\"1\" cellspacing=\"1\" width=\"95%\">");
            sw.WriteLine("<tr class=\"TableHeader\" valign=\"top\"><td width=\"45%\"><b>Name</b></td><td width=\"45%\"><b>List Name</b></td>");
            sw.WriteLine("<td width=\"5%\">Status</td><td width=\"43%\" nowrap=\"nowrap\"><b>Description</b></td><td width=\"5%\" nowrap=\"nowrap\"><b>Time(s)</b></td></tr>");
            int index = 0;
            foreach (Test test in tests)
            {
                if (test.Status == "PASS" || test.Status == "COMPLETED")
                {
                    sw.WriteLine("<tr valign=\"top\" class=\"Pass\"><td width=\"20%\" class=\"method\">");
                    if (null != test.ErrorMessage)
                    {
                        sw.WriteLine("<a title=\"Show/Hide comments\" href=\"javascript:Toggle('" + test.Name + index.ToString() + tableName + "')\" class=\"method\"><u>" + test.Name + "</u></a></td>");
                        sw.WriteLine("<td width=\"10%\">" + test.TestListID + "</td>");
                        sw.WriteLine("<td width=\"10%\" style=\"padding-left:3px\" height=\"9px\"><span class=\"covered\" style=\"width:100px\"></span></td>");
                        sw.WriteLine("<td>" + test.Description + "</td>");
                        sw.WriteLine("<td>" + test.Time.ToString() + "</td></tr>");
                        sw.WriteLine("<tr style=\"display: none;\" id=\"" + test.Name + index.ToString() + tableName + "\">");
                        sw.WriteLine("<td colspan=\"4\" class=\"FailureDetail\">" + test.ErrorMessage.Replace("\r\n", "<br/>") + "</td></tr>");
                    }
                    else
                    {
                        sw.WriteLine(test.Name + "</td>");
                        sw.WriteLine("<td width=\"10%\">" + test.TestListID + "</td>");
                        sw.WriteLine("<td width=\"10%\" style=\"padding-left:3px\" height=\"9px\"><span class=\"covered\" style=\"width:100px\"></span></td>");
                        sw.WriteLine("<td>" + test.Description + "</td>");
                        sw.WriteLine("<td>" + test.Time.ToString() + "</td></tr>");
                    }
                }
                else if (test.Status == "NOTRUN")
                {
                    sw.WriteLine("<tr valign=\"top\" class=\"Ignored\"><td width=\"20%\">" + test.Name + "</td>");
                    sw.WriteLine("<td width=\"10%\" style=\"padding-left:3px\" height=\"9px\"><span class=\"ignored\" style=\"width:100px\"></span></td>");
                    sw.WriteLine("<td>" + test.Description + "</td>");
                    sw.WriteLine("<td>" + test.Time.ToString() + "</td></tr>");
                }
                else
                {
                    if (null != test.ErrorMessage)
                    {
                        sw.WriteLine("<tr valign=\"top\" ><td width=\"20%\">");//class=\"Failure\"
                        sw.WriteLine("<a title=\"Show/Hide error message\" href=\"javascript:Toggle('" + test.Name + index.ToString() + tableName + "')\" class=\"error\">" + test.Name + "</a></td>");
                        sw.WriteLine("<td width=\"10%\">" + test.TestListID + "</td>");
                        sw.WriteLine("<td width=\"10%\" style=\"padding-left:3px\" height=\"9px\"><span class=\"uncovered\" style=\"width:100px\"></span></td>");
                        sw.WriteLine("<td>" + test.Description + "</td>");
                        sw.WriteLine("<td>" + test.Time.ToString() + "</td></tr>");
                        sw.WriteLine("<tr style=\"display: none;\" id=\"" + test.Name + index.ToString() + tableName + "\">");
                        sw.WriteLine("<td colspan=\"4\" class=\"FailureDetail\">" + test.ErrorMessage + "</td></tr>");
                    }
                    else if (null != test.Description)
                    {
                        sw.WriteLine("<tr valign=\"top\" ><td width=\"20%\">");//class=\"Failure\"
                        sw.WriteLine("<a title=\"Show/Hide error message\" href=\"javascript:Toggle('" + test.Name + index.ToString() + tableName + "')\" class=\"error\">" + test.Name + "</a></td>");
                        sw.WriteLine("<td width=\"10%\" style=\"padding-left:3px\" height=\"9px\"><span class=\"uncovered\" style=\"width:100px\"></span></td>");
                        sw.WriteLine("<td>" + test.Description + "</td>");
                        sw.WriteLine("<td>" + test.Time.ToString() + "</td></tr>");
                        sw.WriteLine("<tr style=\"display: none;\" id=\"" + test.Name + index.ToString() + tableName + "\">");
                        sw.WriteLine("<td colspan=\"4\" class=\"FailureDetail\">" + test.Comments + "</td></tr>");
                    }
                    else
                    {
                        sw.WriteLine("<tr valign=\"top\" ><td width=\"20%\">" + test.Name + "</td>");//class=\"Failure\"
                        sw.WriteLine("<td width=\"10%\" style=\"padding-left:3px\" height=\"9px\"><span class=\"uncovered\" style=\"width:100px\"></span></td>");
                        sw.WriteLine("<td>" + test.Description + "</td>");
                        sw.WriteLine("<td>" + test.Time.ToString() + "</td></tr>");
                    }
                }
                index++;
            }
            sw.WriteLine("</table>");
            sw.WriteLine("<a href=\"#top\">Back to top</a>");
        }

        private void CCTable(Module[] modules)
        {
            ////calculate percentages
            double pNotCoveredBlocks;
            double pCoveredBlocks;

            sw.WriteLine("<h3>Code Coverage Summary</h3>");
            sw.WriteLine("<table border=\"0\" cellpadding=\"2\" cellspacing=\"0\" width=\"95%\">");
            sw.WriteLine("<tr class=\"TableHeader\" valign=\"top\">");
            sw.WriteLine("<td width=\"40%\"><b>Name</b></td>");
            sw.WriteLine("<td width=\"10%\"><b>Not Covered (Blocks)</b></td>");
            sw.WriteLine("<td width=\"10%\"><b>%Not Covered (Blocks)</b></td>");
            sw.WriteLine("<td width=\"10%\"><b>Covered (Blocks)</b></td>");
            sw.WriteLine("<td width=\"10%\"><b>%Covered (Blocks)</b></td>");
            sw.WriteLine("</tr>");

            foreach (Module m in modules)
            {
                pNotCoveredBlocks = Convert.ToDouble((Convert.ToInt32(m.blocksNotCovered) * 100)) / Convert.ToDouble((Convert.ToInt32(m.blocksCovered) + Convert.ToInt32(m.blocksNotCovered)));
                pCoveredBlocks = Convert.ToDouble((Convert.ToInt32(m.blocksCovered) * 100)) / Convert.ToDouble((Convert.ToInt32(m.blocksCovered) + Convert.ToInt32(m.blocksNotCovered)));

                sw.WriteLine("<tr valign=\"top\">");
                sw.WriteLine("<td width=\"40%\"><b>" + m.moduleName + "</b></td>");
                sw.WriteLine("<td width=\"10%\">" + m.blocksNotCovered + "</td>");
                sw.WriteLine("<td width=\"10%\">" + Math.Round(pNotCoveredBlocks, 2) + "%" + "</td>");
                sw.WriteLine("<td width=\"10%\">" + m.linesNotCovered + "</td>");
                sw.WriteLine("<td width=\"10%\">" + Math.Round(pCoveredBlocks, 2) + "%" + "</td>");

                foreach (NamespaceTable s in m.namespaces)
                {
                    pNotCoveredBlocks = (Convert.ToDouble(m.blocksNotCovered) * 100) / (Convert.ToDouble(m.blocksCovered) + Convert.ToDouble(m.blocksNotCovered));
                    pCoveredBlocks = (Convert.ToDouble(m.blocksCovered) * 100) / (Convert.ToDouble(m.blocksCovered) + Convert.ToDouble(m.blocksNotCovered));

                    sw.WriteLine("<tr valign=\"top\">");
                    sw.WriteLine("<td width=\"40%\"><b>" + s.name + "</b></td>");
                    sw.WriteLine("<td width=\"10%\">" + s.blocksNotCovered + "</td>");
                    sw.WriteLine("<td width=\"10%\">" + Math.Round(pNotCoveredBlocks, 2) + "%" + "</td>");
                    sw.WriteLine("<td width=\"10%\">" + s.linesNotCovered + "</td>");
                    sw.WriteLine("<td width=\"10%\">" + Math.Round(pCoveredBlocks, 2) + "%" + "</td>");

                    ////commenting for now to simplify summary table
                    //foreach (ClassNode c in s.classes)
                    //{
                    //    sw.WriteLine("<tr valign=\"top\">");

                    //    sw.WriteLine("<td width=\"40%\">" + c.className + "</td>");
                    //    sw.WriteLine("<td width=\"10%\">" + c.blocksNotCovered + "</td>");
                    //    sw.WriteLine("<td width=\"10%\">" + c.blocksCovered + "</td>");
                    //    sw.WriteLine("<td width=\"10%\">" + c.linesNotCovered + "</td>");
                    //    sw.WriteLine("<td width=\"10%\">" + c.linesCovered + "</td>");

                    //    sw.WriteLine("</tr>");
                    //}

                    sw.WriteLine("</tr>");

                }
                sw.WriteLine("</tr>");

            }
            sw.WriteLine("</table>");
            sw.WriteLine("<hr size=\"1\" width=\"95%\" align=\"left\"></hr>");

        }
    }

    class XmlManipulations
    {
        public static string[] GetTestDlls(XmlDocument doc)
        {
            string AllTestDlls = "";
            XmlNodeList nodes = doc.SelectNodes("//TestDefinitions/UnitTest");
            if (nodes.Count <= 0)
            {
                return new string[] { };
            }
            foreach (XmlNode n in nodes)
            {
                string s = n.SelectSingleNode("TestMethod/@codeBase").InnerText;

                int index = s.LastIndexOf("/");
                if (index < s.LastIndexOf("\\"))
                {
                    index = s.LastIndexOf("\\");
                }
                if (index < 0)
                {
                    index = 0;
                }

                string codeBase = s.Substring(index + 1, s.Length - index - 1);
                if (AllTestDlls.IndexOf(codeBase) >= 0)
                {
                    continue;
                }
                AllTestDlls += codeBase + ";";
            }

            AllTestDlls = AllTestDlls.Substring(0, Math.Max(AllTestDlls.LastIndexOf(";"), 0));
            string[] testDlls = AllTestDlls.Split(new char[] { ';' });
            return testDlls;

        }

        public static string[] GetTestNames(XmlDocument doc, string testDll)
        {
            string AllTestNames = "";
            XmlNodeList nodes = doc.SelectNodes("//TestDefinitions/UnitTest");
            foreach (XmlNode n in nodes)
            {
                string sCodeBase = n.SelectSingleNode("TestMethod/@codeBase").InnerText;

                if (sCodeBase != null)
                {
                    int index = sCodeBase.LastIndexOf("/");
                    if (index < sCodeBase.LastIndexOf("\\"))
                    {
                        index = sCodeBase.LastIndexOf("\\");
                    }
                    if (index < 0)
                    {
                        index = 0;
                    }
                    string codeBase = sCodeBase.Substring(index + 1, sCodeBase.Length - index - 1);
                    if (codeBase == testDll)
                    {
                        string testName = n.SelectSingleNode("TestMethod/@name").InnerText;
                        AllTestNames += testName + ";";

                    }
                }
            }

            AllTestNames = AllTestNames.Substring(0, AllTestNames.LastIndexOf(";"));
            string[] testNames = AllTestNames.Split(new char[] { ';' });
            return testNames;

        }

        public static string[] GetTestDescription(XmlDocument doc, string testDll)
        {

            XmlNodeList nodes = doc.SelectNodes("//TestDefinitions/UnitTest");
            string[] AllTestDescriptions = new string[nodes.Count];
            int i = 0;
            foreach (XmlNode n in nodes)
            {
                string sCodeBase = n.SelectSingleNode("TestMethod/@codeBase").InnerText;

                if (sCodeBase != null)
                {
                    int index = sCodeBase.LastIndexOf("/");
                    if (index < sCodeBase.LastIndexOf("\\"))
                    {
                        index = sCodeBase.LastIndexOf("\\");
                    }
                    if (index < 0)
                    {
                        index = 0;
                    }
                    string codeBase = sCodeBase.Substring(index + 1, sCodeBase.Length - index - 1);
                    if (codeBase == testDll)
                    {
                        if (n.InnerXml.Contains("<Description>"))
                        {
                            AllTestDescriptions[i] = n.SelectSingleNode("Description").InnerText;

                        }
                        else
                        {
                            AllTestDescriptions[i] = "NA";
                        }
                        i++;
                    }
                }

            }
            return AllTestDescriptions;
        }

        public static summary GetSummary(XmlDocument doc)
        {
            summary s = new summary();
            s.total = -1;
            s.executed = -1;
            s.passed = -1;

            //XmlElement ele = doc.DocumentElement;

            XmlNode nTotal = doc.SelectNodes("//Counters/@total").Item(0);
            s.total = Convert.ToInt32(nTotal.InnerText);
            XmlNode nPass = doc.SelectNodes("//Counters/@passed").Item(0);
            s.passed = Convert.ToInt32(nPass.InnerText);
            XmlNode nExecuted = doc.SelectNodes("//Counters/@executed").Item(0);
            s.executed = Convert.ToInt32(nExecuted.InnerText);
            ////condition to check if the file has been modified to have elapsedtime attribute
            ////elapsed time attribute will be set when 2 trx files are combined
            if (doc.SelectNodes("//Times/@elapsedtime").Count != 0)
            {
                XmlNode nElapsed = doc.SelectNodes("//Times/@elapsedtime").Item(0);
                s.time = Convert.ToDouble(nElapsed.InnerText);
            }
            else
            {
                DateTime start;
                DateTime end0;
                XmlNode nStart = doc.SelectNodes("//Times/@start").Item(0);
                start = Convert.ToDateTime(nStart.InnerText);
                XmlNode nEnd = doc.SelectNodes("//Times/@finish").Item(0);
                end0 = Convert.ToDateTime(nEnd.InnerText);
                //s.time = Math.Round((double)(end0 - start)/10000000.0,2); //ticks are in 100-nanoseconds
                s.time = Math.Round((end0 - start).TotalSeconds);
            }

            //get execution date
            XmlNode dtDate = doc.SelectNodes("//Times/@creation").Item(0);
            s.execDate = Convert.ToDateTime(dtDate.InnerText);
            return s;
        }

        public static void GetTestPassFails(XmlDocument doc, ref testProjectDetails t)
        {
            XmlNodeList nodes = doc.SelectNodes("//Results/UnitTestResult");
            foreach (XmlNode n in nodes)
            {
                for (int i = 0; i < t.tests.Length; i++)
                {
                    // Same test may be run twice
                    if (t.tests[i].Name == n.SelectSingleNode("@testName").InnerText && t.tests[i].Status == null)
                    {
                        // Get outcome, error message and update failed count if required
                        if (n.SelectSingleNode("@outcome").InnerText == "Passed")
                        {
                            t.tests[i].Status = "PASS";
                            if (n.SelectSingleNode("Output/ErrorInfo/Message") != null && n.SelectSingleNode("Output/ErrorInfo/Message").InnerText != "")
                            {
                                t.tests[i].ErrorMessage = n.SelectSingleNode("Output/ErrorInfo/Message").InnerText;
                            }
                        }
                        else if (n.SelectSingleNode("@outcome").InnerText == "PassedButRunAborted")
                        {
                            t.tests[i].Status = "PASSEDBUTABORTED";

                        }
                        else if (n.SelectSingleNode("@outcome").InnerText == "NotExecuted")
                        {
                            t.tests[i].Status = "NOTRUN";
                        }
                        else
                        {
                            if (n.SelectSingleNode("@outcome").InnerText == "Aborted")
                            {
                                t.tests[i].Status = "ABORTED";

                            }
                            else
                            {
                                t.tests[i].Status = "FAIL";
                            }
                            if (n.SelectSingleNode("Output/ErrorInfo/Message") != null && n.SelectSingleNode("Output/ErrorInfo/Message").InnerText != "")
                            {
                                t.tests[i].ErrorMessage = n.SelectSingleNode("Output/ErrorInfo/Message").InnerText;
                            }
                            t.fail++;
                        }

                        string[] time = n.SelectSingleNode("@duration").InnerText.Split(new char[] { ':' });
                        t.tests[i].Time = Math.Round((Convert.ToInt32(time[0]) * 60 * 60) + (Convert.ToInt32(time[1]) * 60) + Convert.ToDouble(time[2]), 3);
                        break;
                    }

                }

            }
        }

        public static Test[] GetTestDetails(XmlNode rootNode, ResultType result)
        {
            string selectResults;
            if (result == ResultType.TestResultAggregationSubTests)
            {
                selectResults = "innerResults/element";
            }
            else
            {
                selectResults = result.ToString();
            }

            XmlNodeList nodes = rootNode.SelectNodes("//" + selectResults);
            if (nodes.Count <= 0)
            {
                return new Test[] { };
            }
            Test[] tests = new Test[nodes.Count];
            int index = 0;
            foreach (XmlNode n in nodes)
            {
                // Same test may be run twice
                // special handling for manual test cases
                if (selectResults == "ManualTestResult")
                {
                    XmlNodeList mT = rootNode.SelectNodes("//ManualTest");

                    foreach (XmlNode t in mT)
                    {
                        
                        if (t.SelectSingleNode("@name").InnerText == n.SelectSingleNode("@testName").InnerText)
                        {
                            if (t.SelectSingleNode("Comments") != null)
                            {
                                tests[index].Description = t.SelectSingleNode("Description").InnerText;
                                break;
                            }
                            else if (t.SelectSingleNode("Description") != null)
                            {
                                tests[index].Description = t.SelectSingleNode("Description").InnerText;
                                break;
                            }
                            else
                            {
                                tests[index].Description = "Description not set";
                                break;
                            }
                        }
                    }
                }
                tests[index].Name = n.SelectSingleNode("@testName").InnerText;
                XmlNodeList lst = rootNode.SelectNodes("//TestList");
                foreach (XmlNode b in lst)
                {
                    if (n.SelectSingleNode("@testListId").InnerText == b.SelectSingleNode("@id").InnerText)
                    {
                        tests[index].TestListID = b.SelectSingleNode("@name").InnerText;
                    }
                }
                
                // Get outcome, error message and update failed count if required
                if (n.SelectSingleNode("@outcome").InnerText == "Passed")
                {
                    tests[index].Status = "PASS";

                }
                else if (n.SelectSingleNode("@outcome").InnerText == "PassedButRunAborted")
                {
                    tests[index].Status = "PASSEDBUTABORTED";

                }
                else if (n.SelectSingleNode("@outcome").InnerText == "NotExecuted")
                {
                    tests[index].Status = "NOTRUN";
                }
                else
                {
                    if (n.SelectSingleNode("@outcome").InnerText == "Aborted")
                    {
                        tests[index].Status = "ABORTED";

                    }
                    else
                    {
                        tests[index].Status = "FAIL";
                    }
                    if (null != n.SelectSingleNode("Output/ErrorInfo/Message") && n.SelectSingleNode("Output/ErrorInfo/Message").InnerText.Length > 0)
                    {
                        tests[index].ErrorMessage = n.SelectSingleNode("Output/ErrorInfo/Message").InnerText;
                    }

                }
                if (null != n.SelectSingleNode("Comments") && n.SelectSingleNode("Comments").InnerText.Length > 0)
                {
                    tests[index].ErrorMessage = n.SelectSingleNode("Comments").InnerText;
                }
                string[] time = n.SelectSingleNode("@duration").InnerText.Split(new char[] { ':' });
                tests[index].Time = Math.Round((Convert.ToInt32(time[0]) * 60 * 60) + (Convert.ToInt32(time[1]) * 60) + Convert.ToDouble(time[2]), 3);
                index++;
            }
            return tests;
        }

        public static int GetResultCount(XmlDocument doc, ResultType result)
        {
            try
            {
                return doc.SelectNodes("//Results/" + result.ToString()).Count;
            }
            catch
            {
                return 0;
            }
        }

        public static XmlNode GetOrderedTestsRoot(XmlDocument doc, string testName)
        {
            try
            {
                foreach (XmlNode n in doc.SelectNodes("//TestDefinitions/UnitTest"))
                {
                    if (n.SelectSingleNode("@name").InnerText.ToLower() == testName.ToLower())
                    {
                        return n;
                    }
                }
            }
            catch
            {
                //
            }
            return null;

        }

        ////XML manipulation for CC data
        public static Module[] GetCCData(string codecoverageXML)
        {
            try
            {
                XmlDocument oCC = new XmlDocument();
                oCC.Load(codecoverageXML);

                XmlNodeList modules = oCC.SelectNodes("//Module");
                Module[] oModule = new Module[modules.Count];
                int index = 0;

                foreach (XmlNode m in modules)
                {
                    oModule[index].moduleName = m.SelectSingleNode("ModuleName").InnerText;
                    oModule[index].linesCovered = m.SelectSingleNode("LinesCovered").InnerText;
                    oModule[index].linesNotCovered = m.SelectSingleNode("LinesNotCovered").InnerText;
                    oModule[index].linesPartiallyCovered = m.SelectSingleNode("LinesPartiallyCovered").InnerText;
                    oModule[index].blocksCovered = m.SelectSingleNode("BlocksCovered").InnerText;
                    oModule[index].blocksNotCovered = m.SelectSingleNode("BlocksNotCovered").InnerText;

                    XmlNodeList spaces = m.SelectNodes("NamespaceTable");
                    oModule[index].namespaces = new NamespaceTable[spaces.Count];
                    int s = 0;

                    foreach (XmlNode n in spaces)
                    {
                        oModule[index].namespaces[s].blocksCovered = n.SelectSingleNode("BlocksCovered").InnerText;
                        oModule[index].namespaces[s].blocksNotCovered = n.SelectSingleNode("BlocksNotCovered").InnerText;
                        oModule[index].namespaces[s].linesCovered = n.SelectSingleNode("LinesCovered").InnerText;
                        oModule[index].namespaces[s].linesNotCovered = n.SelectSingleNode("LinesNotCovered").InnerText;
                        oModule[index].namespaces[s].linesPartiallyCovered = n.SelectSingleNode("LinesPartiallyCovered").InnerText;
                        oModule[index].namespaces[s].moduleName = n.SelectSingleNode("ModuleName").InnerText;
                        oModule[index].namespaces[s].key = n.SelectSingleNode("NamespaceKeyName").InnerText;
                        oModule[index].namespaces[s].name = n.SelectSingleNode("NamespaceName").InnerText;

                        XmlNodeList classes = n.SelectNodes("Class");
                        oModule[index].namespaces[s].classes = new ClassNode[classes.Count];
                        int p = 0;

                        foreach (XmlNode c in classes)
                        {
                            oModule[index].namespaces[s].classes[p].blocksCovered = c.SelectSingleNode("BlocksCovered").InnerText;
                            oModule[index].namespaces[s].classes[p].blocksNotCovered = c.SelectSingleNode("BlocksNotCovered").InnerText;
                            oModule[index].namespaces[s].classes[p].linesCovered = c.SelectSingleNode("LinesCovered").InnerText;
                            oModule[index].namespaces[s].classes[p].linesNotCovered = c.SelectSingleNode("LinesNotCovered").InnerText;
                            oModule[index].namespaces[s].classes[p].linesPartiallyCovered = c.SelectSingleNode("LinesPartiallyCovered").InnerText;
                            oModule[index].namespaces[s].classes[p].className = c.SelectSingleNode("ClassName").InnerText;
                            oModule[index].namespaces[s].classes[p].namespaceKeyName = c.SelectSingleNode("NamespaceKeyName").InnerText;
                            oModule[index].namespaces[s].classes[p].classKeyName = c.SelectSingleNode("ClassKeyName").InnerText;

                            p++;
                        }
                        s++;

                    }
                    index++;
                }
                return oModule;
            }
            catch(Exception ex)
            {
                return null;
            }
        }

        ////get all test list names and associated test cases


    }

    struct testProjectDetails
    {
        public string testDll;
        public Test[] tests;
        public int count;
        public int fail;
    }

    struct Test
    {
        public string Name;
        public string Status;
        public string ErrorMessage;
        public double Time;
        public string Description;
        //special handling for manual test case
        public string Comments;
        public string TestListID;
    }

    struct summary
    {
        public int total;
        public int executed;
        public int passed;
        public double time;
        public DateTime execDate;
    }

    public enum ResultType { ManualTestResult, WebTestResult, GenericTestResult, UnitTestResult, LoadTestResult, TestResultAggregation, TestResultAggregationSubTests };

    struct Module
    {
        public string moduleName;
        public string linesCovered;
        public string linesPartiallyCovered;
        public string linesNotCovered;
        public string blocksCovered;
        public string blocksNotCovered;
        public NamespaceTable[] namespaces;
    }

    struct NamespaceTable
    {
        public string name;
        public string key;
        public string moduleName;
        public string linesCovered;
        public string linesPartiallyCovered;
        public string linesNotCovered;
        public string blocksCovered;
        public string blocksNotCovered;
        public ClassNode[] classes;
    }

    struct ClassNode
    {
        public string className;
        public string classKeyName;
        public string namespaceKeyName;
        public string linesCovered;
        public string linesPartiallyCovered;
        public string linesNotCovered;
        public string blocksCovered;
        public string blocksNotCovered;
    }
}
